package asteroids.model;

import be.kuleuven.cs.som.annotate.*;
import asteroids.Util;
import asteroids.Vector;

/**
 * A superclass with space objects which have coordinates, velocities, a mass, a world and a radius.
 * 
 * @author Thijs Dieltjens - Wim Konings
 * |2BCwaElt
 * |https://asteroids-ogp.googlecode.com/svn/trunk/
 * @version 2.0
 * 
 * @invar 	The space object has valid coordinates.
 * 			|isValidCoordinate(coordinates)
 * @invar	The space object has a valid speed in both directions.
 * 			|isValidVelocity(velocities)
 * @invar	The space object has a valid radius.
 * 			|isValidRadius(radius)
 * @invar	The space object has a valid mass.
 * 			|isValidMass(mass)
 */

public abstract class SpaceObject {

	/**
	 * Creates a SpaceObject instance with given coordinates, velocities, mass, radius and world.
	 * 
	 * @param 	coordinates
	 * 			The vector of coordinates for this space object.
	 * @param 	velocities
	 * 			The vector of velocities for this space object.
	 * @param 	mass
	 * 			The mass of this space object.
	 * @param 	world
	 * 			The world of this space object.
	 * @param	radius
	 * 			The radius of this space object.
	 * @throws 	IllegalArgumentException
	 * 			The coordinates or velocities of this space object aren't valid.
	 * 			| (!isValidCoordinate(coordinates) || !isValidVelocity(velocities) || !canHaveAsPosition(coordinates))
	 * @throws 	IllegalArgumentException
	 * 			The radius or massof this space object isn't valid.
	 * 			| (!isValidRadius(radius) || !isValidMass(mass))
	 * @post	All the attributes of this space object are equal to their corresponding parameter.
	 * 			| new.getCoordinates() == coordinates && new.getVelocities() == velocities &&
	 * 			| new.getDirection() == direction &&  new.getMass() == mass &&
	 * 			| new.getWorld == world && new.getRadius() == radius
	 */
	protected SpaceObject(Vector coordinates, Vector velocities, double mass, World world, double radius) throws IllegalArgumentException{

		this.maxSpeed=SPEED_OF_LIGHT;

		if(canHaveAsCoordinates(coordinates) && canHaveAsVelocity(velocities) && canHaveAsPosition(coordinates)) {
			setCoordinates(coordinates);
			setVelocities(velocities);
		}
		else{
			throw new IllegalArgumentException();
		}
		if(isValidRadius(radius) && isValidMass(mass)){
			this.radius=radius;
		}
		else{
			throw new IllegalArgumentException();
		}
		this.mass = mass;
		this.world = world;
	}

	/**
	 * Creates a SpaceObject instance with given coordinates, velocities, mass and radius. 
	 * 
	 * @param 	coordinates
	 * 			The vector of coordinates for this space object.
	 * @param 	velocities
	 * 			The vector of velocities for this space object.
	 * @param 	mass
	 * 			The mass of this space object.
	 * @param 	world
	 * 			The world of this space object.
	 * @param	radius
	 * 			The radius of this space object.
	 * @post	All the attributes of this space object are equal to their corresponding parameter.
	 * 			| new.getCoordinates() == coordinates && new.getVelocities() == velocities &&
	 * 			| new.getMass() == mass && new.getWorld == null && new.getRadius() == radius
	 */
	protected SpaceObject(Vector coordinates, Vector velocities, double mass, double radius) {
		this(coordinates, velocities, mass, null, radius);
	}

	/**
	 * Returns the vector of coordinates of this space object.
	 */
	@Basic
	public Vector getCoordinates() {
		return coordinates;
	}

	/**
	 * Checks if the given vector of coordinates is valid.
	 * 
	 * @param 	coordinates
	 * 			The given vector of coordinates.
	 * @return	True if and only if the x- and y-coordinate are positive numbers, but not equal to infinity.
	 * 			| result == !( xCoordinate.isNaN() || xCoordinate==Double.POSITIVE_INFINITY || Util.fuzzyLessThan(xCoordinate, getRadius())
	 *			|				|| yCoordinate.isNaN() || yCoordinate==Double.POSITIVE_INFINITY || Util.fuzzyLessThan(yCoordinate, getRadius()) )
	 */
	public boolean canHaveAsCoordinates(Vector coordinates) {
		Double xCoordinate = coordinates.getXCoordinate();
		Double yCoordinate = coordinates.getYCoordinate();
		return !(xCoordinate.isNaN() || xCoordinate==Double.POSITIVE_INFINITY || Util.fuzzyLessThan(xCoordinate, getRadius())
				|| yCoordinate.isNaN() || yCoordinate==Double.POSITIVE_INFINITY || Util.fuzzyLessThan(yCoordinate, getRadius()));
	}
	/**
	 * Sets the coordinates of this space object.
	 * 
	 * @param 	coordinates 
	 * 			The coordinates to set for this space object.
	 * @post	The coordinates for this space object has the same value as the inserted coordinates. 
	 * 			| new.getCoordinates() == coordinates
	 * @throws	IllegalArgumentException
	 * 			The x-coordinate and/or y-coordinate is not a valid coordinate. 
	 * 			| if(!isValidCoordinate(coordinates))
	 * @note	This method is written defensively. 
	 */
	public void setCoordinates(Vector coordinates) throws IllegalArgumentException {
		if(!canHaveAsCoordinates(coordinates)) {
			throw new IllegalArgumentException();
		}
		this.coordinates = coordinates;
	}

	private Vector coordinates;

	/**
	 * Returns the vector of velocities of this space object.
	 */
	@Basic
	public Vector getVelocities() {
		return velocities;
	}

	/**
	 * Returns the maxSpeed of this space object.
	 */
	@Basic @Immutable
	public double getMaxSpeed() {
		return maxSpeed;
	}

	/**
	 * Checks if the given vector of velocities is valid.
	 * 
	 * @param 	velocities
	 * 			The velocities which needs to be checked.
	 * @return 	True if the vector is valid.
	 * 			| result == ( !( velocities.getXCoordinate().isNaN() || velocities.getXCoordinate() < -getMaxSpeed()
	 * 			|				|| getMaxSpeed() < velocities.getXCoordinate() )
	 * 			| 			&& !( (velocities.getYCoordinate().isNaN() || velocities.getYCoordinate() < -getMaxSpeed()
	 * 			|				|| getMaxSpeed() < velocities.getYCoordinate()) ) )
	 */
	public boolean canHaveAsVelocity(Vector velocities){
		Double xVelocity = velocities.getXCoordinate();
		Double yVelocity = velocities.getYCoordinate();
		if(xVelocity.isNaN() || Util.fuzzyLessThan(xVelocity,-getMaxSpeed()) || Util.fuzzyLessThan(getMaxSpeed(),xVelocity)){
			return false;
		}
		if(yVelocity.isNaN() || Util.fuzzyLessThan(yVelocity,-getMaxSpeed()) || Util.fuzzyLessThan(getMaxSpeed(),yVelocity)){
			return false;
		}
		return true;
	}

	/**
	 * Sets the vector of velocities of this space object.
	 * 
	 * @param	givenVelocities
	 * 			The velocities to set for this space object.
	 * @post	If one of the given velocities is NaN then this space object's velocities in all directions will be set at zero.
	 * 			| if(velocities.getXCoordinate().isNaN())
	 * 			| 	then new.getVelocities().getXCoordinate() == 0
	 * 			| if(velocities.getYCoordinate().isNaN()))
	 * 			|	then new.getVelocities().getYCoordinate() == 0
	 * @post	The lowest possible value for the velocity in all directions of this space object
	 * 			is equal to the negative of the predefined maximum speed of this space object.
	 * 			| if(velocities.getXCoordinate() < -getMaxSpeed())
	 * 			|	then new.getVelocities().getXCoordinate() == -getMaxSpeed()
	 * 			| if(velocities.getYCoordinate() < -getMaxSpeed())
	 * 			|	then new.getVelocities().getYCoordinate == -getMaxSpeed()
	 * @post	The highest possible value for the velocity in all directions of this space object 
	 * 			is equal to the predefined maximum speed of this space object. 
	 * 			| if(velocities.getXCoordinate() > getMaxSpeed())
	 * 			|	then new.getVelocities().getXCoordinate() == getMaxSpeed()
	 * 			| if(velocities.getYCoordinate() > getMaxSpeed())
	 * 			|	then new.getVelocities().getYCoordinate() == getMaxSpeed()
	 * @post	If the given velocity in all directions is in the range 0..maxSpeed, 
	 * 			the velocity in all directions of this space object is equal to the given vector of velocities
	 * 			| if( (velocities.getXCoordinate() <= getMaxSpeed()) && (velocities.getXCoordinate() >= -getMaxSpeed())
	 * 			|		&& (velocities.getYCoordinate() <= getMaxSpeed()) && (velocities.getYCoordinate() >= -getMaxSpeed()) )
	 * 			|	then new.getVelocities() == velocities
	 * @note	This method is written totally.
	 * */
	public void setVelocities(Vector givenVelocities) {
		Double xVelocity = givenVelocities.getXCoordinate();
		Double yVelocity = givenVelocities.getYCoordinate();
		if(xVelocity.isNaN()){
			xVelocity = 0.0;
		}
		else if(Util.fuzzyLessThan(xVelocity,-getMaxSpeed())) {
			xVelocity = -getMaxSpeed();
		}
		else if(Util.fuzzyLessThan(getMaxSpeed(),xVelocity)){
			xVelocity = getMaxSpeed();
		}

		if(yVelocity.isNaN()){
			yVelocity = 0.0;
		}
		else if(Util.fuzzyLessThan(yVelocity,-getMaxSpeed())) {
			yVelocity = -getMaxSpeed();
		}
		else if(Util.fuzzyLessThan(getMaxSpeed(),yVelocity)){
			yVelocity = getMaxSpeed();
		}

		Vector newVelocities = new Vector(xVelocity,yVelocity);
		this.velocities= newVelocities;

	}

	private Vector velocities; 
	private final double maxSpeed;

	/**
	 * Returns the speed of light.
	 * 
	 * @return	The speed of light.
	 * 			| result == 300000.0
	 */
	public static final double SPEED_OF_LIGHT=300000.0;

	/**
	 * Returns the radius of this space object.
	 */
	@Basic @Immutable
	public double getRadius(){
		return radius;
	}

	/**
	 * Checks if the given radius of this space object is valid.
	 * 
	 * @param 	radius
	 * 			The given radius for this space object.
	 * @return	True if valid radius, false if not valid (Not a number, infinite or smaller than the minimal radius).
	 * 			| result == ( (!radius.isNaN() && !radius.isInfinite() && !(radius<=minRadius)) )
	 */
	public static boolean isValidRadius(Double radius){
		if(radius.isNaN() || radius.isInfinite() || Util.fuzzyLessThanOrEqualTo(radius,MINRADIUS)) {
			return false;
		}
		return true;
	}

	private final double radius;

	/**
	 * Returns the minimum radius of this space object.
	 * 
	 * @return	The minimum radius of this space object.
	 * 			| result == 0
	 */
	private static final double MINRADIUS = 0;

	/**
	 * Returns the mass of this space object.
	 */
	@Basic @Immutable
	public double getMass(){
		return mass;
	}

	/**
	 * Checks if the given mass is valid or not.
	 * 
	 * @param 	mass
	 * 			The given mass of this space object.
	 * @return	True if and only if the mass is a valid, strictly positive number, but not infinity.
	 * 			| result == !( (mass.isNaN() || mass==Double.POSITIVE_INFINITY || Util.fuzzyLessThanOrEqualTo(mass, 0)) )
	 */
	public static boolean isValidMass(Double mass) {
		return !(mass.isNaN() || mass==Double.POSITIVE_INFINITY || Util.fuzzyLessThanOrEqualTo(mass, 0));
	}

	private final double mass;

	/**
	 * Returns the world this space object is in.
	 */
	@Basic
	public World getWorld(){
		return world;
	}

	/**
	 * Sets the world of this space object.
	 * 
	 * @param 	world
	 * 			The world of this space object.
	 */
	public void setWorld(World world) {
		this.world = world;
	}

	private World world;

	/**
	 * Checks if the given duration is valid. 
	 * 
	 * @param 	duration
	 * 			The given duration.
	 * @return	True if and only if the given duration is valid. That's the fact when
	 * 			the duration is a rational number and not infinity.
	 * 			| if((duration.isNaN()) || (duration == Double.POSITIVE_INFINITY))
	 * 			|	then result == false
	 * @return	True if and only if the given duration is positive
	 * 			| if(duration<0) 
	 * 			| 	then result == false
	 * 			| else result == true
	 */
	public static boolean isValidDuration(Double duration) {
		if((duration.isNaN()) || (duration == Double.POSITIVE_INFINITY)) {
			return false;
		}
		else if(Util.fuzzyLessThan(duration,0)) {
			return false;
		}
		else {
			return true; 
		}
	}


	/**
	 * Moves the ship to a certain position based on his actual position, his
	 * velocity and his given time duration. 
	 * 
	 * @param 	duration
	 * 			The duration that this space object moves.
	 * @post	The vector of coordinates is changed  if the new position is valid. 
	 * 			| new.getCoordinates == getPositionAfterMove(duration)
	 * @throws 	IllegalArgumentException
	 * 			The duration is not a valid number.
	 * 			| (!isValidDuration(duration))
	 * @throws 	IllegalStateException
	 * 			The resulting position isn't valid.
	 * 			| setCoordinates(getPositionAfterMove(duration)))
	 */
	public void move(double duration) throws IllegalArgumentException {
		if(isValidDuration(duration)){
			try{
				Vector newPosition = getPositionAfterMove(duration);				
				this.setCoordinates(newPosition);

			}
			catch(IllegalStateException e) {
				// do nothing
			}
		}
		else{
			throw new IllegalArgumentException();
		}
	}

	/**
	 * This method checks if this space object overlaps with another space object.
	 * 
	 * @param 	spaceObject 
	 * 			The space object you want to check the overlap with.
	 * @return	True if there's overlap, or the given space object is this space object.
	 * 			| if(this!=spaceObject)
	 * 			| 	then result == (getDistanceBetween(spaceObject)<=0)
	 * 			| else result == true
	 * @throws	IllegalStateException
	 * 			see getDistanceBetween(spaceObject)
	 * @throws	NullPointerException
	 * 			The given space object is null.
	 * 			| spaceObject == null
	 * @note	This method is written defensively. 
	 */
	public boolean overlap(SpaceObject spaceObject) throws NullPointerException,IllegalStateException{
		try{
			if(this!=spaceObject){
				return Util.fuzzyLessThan(getDistanceBetween(spaceObject),0);
			}
			else{
				return true;
			}
		}
		catch(IllegalStateException | NullPointerException npe) {
			throw npe; 
		}
	}

	/**
	 * Calculates the time until there is a collision between 2 space objects, given a difference in velocity, 
	 * a difference in coordinates and the sum of radii.
	 * 
	 * @param 	veloDiff
	 * 			The difference in velocities.
	 * @param	posDiff
	 * 			The difference in coordinates.
	 * @param 	sumOfradii
	 * 			The sum of the radii of both space objects.
	 * @return	An infinite amount of time when one of the dotproducts is infinite.
	 * 			| if( Math.abs(veloDiff.dotProduct(veloDiff)) > Double.MAX_VALUE
	 * 			|		|| Math.abs(posDiff.dotProduct(posDiff)) > Double.MAX_VALUE
	 * 			|		|| Math.abs(veloDiff.dotProduct(posDiff)) > Double.MAX_VALUE )
	 * 			| 	then result == Double.POSITIVE_INFINITY
	 * @return	An infinite amount of time when the dotproduct of velocities and coordinates is greater or equal to 0 or when
	 * 			the dotproduct of velocities and velocities is equal to 0.
	 * 			| if((getVelocities().dotProduct(posDiff) >= 0) || (getVelocities().dotProduct(getVelocities()) ==0))
	 * 			|	then result == Double.POSITIVE_INFINITY
	 * @return	An infinite amount of time when the variable temp is infinite or smaller or equal to 0. The meaning of temp will become clear in the lines below.
	 * 			| if( Math.abs( Math.pow(getVelocities().dotProduct(posDiff),2) - getVelocities().dotProduct(getVelocities())
	 * 			|		* (posDiff.dotProduct(posDiff) - Math.pow(sumOfradii,2)) ) > Doule.MAX_VALUE
	 * 			|	|| ( Math.pow(getVelocities().dotProduct(posDiff),2) - getVelocities().dotProduct(getVelocities())
	 * 			|		* (posDiff.dotProduct(posDiff) - Math.pow(sumOfradii,2)) ) <= 0 )
	 * 			| 	then result == Double.POSITIVE_INFINITY
	 * @return	The time until two space objects with a given difference in velocity, difference in coordinates 
	 * 			and sum of radii are going to collide.
	 * 			| result == -( veloDiff.dotProduct(posDiff) + Math.sqrt(Math.pow(veloPosDotProduct,2) 
	 * 			|				- veloDiff.dotProduct(getVelocities())*(posDiff.dotProduct(posDiff) - Math.pow(sumOfradii,2)))) 
	 * 			|			/ (getVelocities().dotProduct(getVelocities())) )
	 * @note	This method is written totally.
	 */
	public double getTime(Vector veloDiff, Vector posDiff, double sumOfRadii) {
		Double veloVeloDotProduct = veloDiff.dotProduct(veloDiff);
		Double posPosDotProduct = posDiff.dotProduct(posDiff);
		Double veloPosDotProduct = veloDiff.dotProduct(posDiff);

		if(posPosDotProduct.isInfinite() || veloPosDotProduct.isInfinite() || veloVeloDotProduct.isInfinite()){
			return Double.POSITIVE_INFINITY;
		}
		Double temp = Math.pow(veloPosDotProduct,2) - veloVeloDotProduct*(posPosDotProduct-Math.pow(sumOfRadii,2));

		if(Util.fuzzyLessThanOrEqualTo(0, veloPosDotProduct) || Util.fuzzyEquals(veloVeloDotProduct, 0)){
			return Double.POSITIVE_INFINITY;
		}
		if (Util.fuzzyLessThanOrEqualTo(temp, 0) || temp.isInfinite()) {
			return Double.POSITIVE_INFINITY;
		}

		else{
			double result = -(veloPosDotProduct+Math.sqrt(temp))/(veloVeloDotProduct);
			return result;
		}		
	}

	/**
	 * This method calculates the time before this spaceObject collapses with another spaceObject. 
	 * 
	 * @param 	spaceObject
	 * 			The space object you want to check the time to collision with.
	 * @return	The time until this space object collides with the given spaceObject.
	 * 			| result == getTime( spaceObject.getVelocities().subtractVector(this.getVelocities()) , 
	 * 			|				spaceObject.getCoordinates().subtractVector(this.getCoordinates()) ,
	 * 			|				this.getRadius() + spaceObject.getRadius() )
	 * @throws 	NullPointerException
	 * 			The given space object is null.
	 * 			| (spaceObject==null)
	 * @throws 	IllegalStateException
	 * 			One of the calculations in getTime() gives an error
	 * 			| getTime( spaceObject.getVelocities().subtractVector(this.getVelocities()) , 
	 * 			|			spaceObject.getCoordinates().subtractVector(this.getCoordinates()) ,
	 * 			|			this.getRadius() + spaceObject.getRadius() )
	 * @note	This method is written defensively.
	 */
	public double getTimeToCollision(SpaceObject spaceObject) throws IllegalStateException,NullPointerException{
		if(spaceObject==null){
			throw new NullPointerException();
		}

		Vector velocityDifference = spaceObject.getVelocities().subtractVector(this.getVelocities());
		Vector positionDifference = spaceObject.getCoordinates().subtractVector(this.getCoordinates());
		Double sigma = this.getRadius() + spaceObject.getRadius();
		if(sigma.isInfinite()){
			throw new IllegalStateException();
		}
		try{
			return getTime(velocityDifference, positionDifference, sigma);		
		}
		catch (Exception e){
			throw new IllegalStateException(); 
		}		
	}

	/**
	 * Returns the time until this spaceObject collides with a world boundary.
	 * 
	 * @note	Below, we refer to the time a space object (virtually) collides with a horizontal boundary as timeHorizontal
	 * 			and to the time a space object (virtually) collides with a vertical boundary as timeVertical.
	 * 			| if(getVelocities().getXCoordinate() != 0) 
	 * 			|	then timeHorizontal == Math.max( (-getCoordinates().getXCoordinate()+getRadius())/getVelocities().getXCoordinate() , 
	 * 			|									(this.getWorld().getSize().getXCoordinate()-getCoordinates().getXCoordinate()-getRadius())/getVelocities().getXCoordinate() )
	 * 			| if(getVelocities().getYCoordinate() != 0)
	 * 			|	then timeVertical == Math.max( (this.getWorld().getSize().getYCoordinate()-getCoordinates().getYCoordinate() -getRadius())/getVelocities().getYCoordinate() , 
	 * 			|									(-getCoordinates().getYCoordinate() + getRadius())/getVelocities().getYCoordinate() )
	 * @return	An infinite amount of time when the x or y velocity are both zero. 
	 * 			| if( getVelocities().getXCoordinate()==0 && getVelocities().getYCoordinate()==0 )
	 * 			|	then result == Double.POSITIVE_INFINITY
	 * @return	An infinite amount of time when there won't be a collision with one of the boundaries. 
	 * 			| if(timeHorizontal < 0 && timeVertical <0)
	 * 			|	then result == Double.POSITIVE_INFINITY
	 * @return	The verticalTime when there would be a (virtual) collision with the vertical boundary and when there won't be a collision 
	 * 			with the horizontal boundary.
	 * 			| if(timeHorizontal<0 && timeVertical>0)
	 * 			|	then result == timeVertical
	 * @return	The timeHorizontal when there would be a (virtual) collision with the horizontal boundary and when there won't be a collision 
	 * 			with the vertical boundary.
	 * 			| if(timeVertical<0 && timeHorizontal>0)
	 * 			|	then result == timeHorizontal
	 * @return	The smallest time of the timeHorizontal and timeVertical if there would be a (virtual) collision with both boundaries.
	 * 			| if(timeVertical>0 && timeHorizontal>0)
	 * 			|	then result == Math.min(timeHorizontal, timeVertical)
	 * @return	If this space object is not in a world the result is positive infinity.
	 * 			| if(this.getWorld()==null)
	 * 			| then result == Double.POSITIVE_INFINITY
	 * @note	This method is written totally.
	 */
	public double getTimeToCollisionBoundary() {
		if(this.getWorld()==null){
			return Double.POSITIVE_INFINITY;
		}
		if(Util.fuzzyEquals(getVelocities().getXCoordinate(),0) && Util.fuzzyEquals(getVelocities().getYCoordinate(),0)){
			return Double.POSITIVE_INFINITY;
		}

		double width = this.getWorld().getSize().getXCoordinate();
		double length = this.getWorld().getSize().getYCoordinate();
		double timeHorizontal = -1;
		double timeVertical = -1;

		//Horizontal collision
		if(!Util.fuzzyEquals(getVelocities().getXCoordinate(),0)){
			double timeLeft = (-getCoordinates().getXCoordinate()+getRadius())/getVelocities().getXCoordinate();
			timeHorizontal = (width-getCoordinates().getXCoordinate()-getRadius())/getVelocities().getXCoordinate();
			timeHorizontal = Math.max(timeLeft, timeHorizontal);
		}

		//Vertical collision
		if(!Util.fuzzyEquals(getVelocities().getYCoordinate(),0)){
			double timeUp = (length-getCoordinates().getYCoordinate() -getRadius())/getVelocities().getYCoordinate();
			timeVertical = (-getCoordinates().getYCoordinate() + getRadius())/getVelocities().getYCoordinate();
			timeVertical = Math.max(timeUp, timeVertical);
		}

		//End result
		double result = timeHorizontal;

		if(Util.fuzzyLessThan(timeHorizontal,0)){
			result = timeVertical;
		}
		else if(Util.fuzzyLessThanOrEqualTo(0,timeVertical)){
			result = Math.min(timeHorizontal, timeVertical);
		}

		if(Util.fuzzyLessThan(result,0)){
			return Double.POSITIVE_INFINITY;
		}

		return result;
	}

	/**
	 * This method calculates the distance between this space object and another space object.
	 * 
	 * @param 	spaceObject
	 * 			The space object you want to compare distance with.
	 * @return	The mathematical distance between the two ships.
	 * 			| result == ( this.getCoordinates().distanceTo(spaceObject.getCoordinates()) -spaceObject.getRadius()-this.getRadius() )
	 *
	 */
	public double getDistanceBetween(SpaceObject spaceObject){	
		return this.getCoordinates().distanceTo(spaceObject.getCoordinates()) -spaceObject.getRadius()-this.getRadius();
	}

	/**
	 * Checks whether a given vector is a valid position for this space object in this world.
	 * 
	 * @param 	position
	 * 			The position vector for this space object.
	 * @return	False if this ship is in a world but would not be in its boundaries at the given position.
	 * 			| if (getWorld()!=null)
	 * 			|	then result == (this.getWorld().isInWorldBoundaries(position, this.radius))
	 * 			| else result == isValidCoordinate(position)
	 */
	public boolean canHaveAsPosition(Vector position) {
		if(getWorld()!=null){
			return this.getWorld().isInWorldBoundaries(position, this.getRadius());
		}

		return canHaveAsCoordinates(position);
	}

	/**
	 * Returns a vector of the new position after the space object moved during a given moveTime.
	 * 
	 * @param 	moveTime
	 * 			The time this ship moves.
	 * @return 	The new position of this ship when it moves for a duration of moveTime.
	 * 			| result ==  new Vector(this.getCoordinates().getXCoordinate()+moveTime*this.getVelocities().getXCoordinate(),
	 * 			|						this.getCoordinates().getYCoordinate()+moveTime*this.getVelocities().getYCoordinate() )
	 * @throws 	IllegalStateException
	 * 			The position calculated isn't a valid position for this spaceObject.
	 * 			| !canHaveAsPosition(result)
	 */
	public Vector getPositionAfterMove(double moveTime) throws IllegalStateException{
		double xAfterMove = this.getCoordinates().getXCoordinate()+moveTime*this.getVelocities().getXCoordinate();
		double yAfterMove = this.getCoordinates().getYCoordinate()+moveTime*this.getVelocities().getYCoordinate();

		Vector result = new Vector(xAfterMove, yAfterMove);

		if(!canHaveAsPosition(result)){
			throw new IllegalStateException();
		}
		return result;
	}

	/**
	 * This method gives a vector with the x- and y-coordinates of the collision position between
	 * this ship and another space object. 
	 * 
	 * @param 	spaceObject
	 * 			The space object you want to get the collision position with this ship of.
	 * @return	A vector with x- and y-coordinates of the collision position.
	 * 			| result == new Vector(this.getPositionAfterMove(time).getXCoordinate()+spaceObject.getPositionAfterMove(time).subtractVector(this.getPositionAfterMove(time)).getXCoordinate()*this.getRadius()/(this.getRadius()+spaceObject.getRadius()) , 
	 *			|						this.getPositionAfterMove(time).getYCoordinate()+spaceObject.getPositionAfterMove(time).subtractVector(this.getPositionAfterMove(time)).getYCoordinate()*this.getRadius()/(this.getRadius()+spaceObject.getRadius()))
	 * @return	If there is no collision, null is returned.
	 * 			| if (getTimeToCollision(spaceObject).isInfinite())
	 * 			|	then result == null
	 * @throws	NullPointerException
	 * 			The given spaceObject is null.
	 * 			| spaceObject==null
	 * @note	This method is written defensively.
	 */
	public Vector getCollisionPosition(SpaceObject spaceObject) throws NullPointerException,IllegalStateException{
		Double time;
		if(spaceObject==null){
			throw new NullPointerException();
		}

		time = getTimeToCollision(spaceObject);
		if(time.isInfinite()){
			return null;
		}
		try{
			Vector newPositionThis = this.getPositionAfterMove(time);
			Vector newPositionOther = spaceObject.getPositionAfterMove(time);
			double relativeSigma = this.getRadius()/(this.getRadius()+spaceObject.getRadius());			
			Vector difference=(newPositionOther.subtractVector(newPositionThis));

			Double xColPosition = newPositionThis.getXCoordinate()+difference.getXCoordinate()*relativeSigma;
			Double yColPosition = newPositionThis.getYCoordinate()+difference.getYCoordinate()*relativeSigma;
			Vector result = new Vector(xColPosition, yColPosition);
			return result;
		}
		catch(IllegalStateException e){
			return null;
		}
	}


	/**
	 * Calculates the collision position with one of the world's boundaries.
	 * 
	 * @note	Below, we refer to the x-coordinate where a space object would collide if it would collide with a vertical boundary as XColPosition and
	 * 			the y-coordinate where a space object would collide if it would collide with a horizontal boundary.
	 * 			| xColPosition = this.getPositionAfterMove(getTimeToCollisionBoundary()).getXCoordinate() + Util.getSign(getVelocities().getXCoordinate())*this.getRadius()
	 *			| yColPosition = this.getPositionAfterMove(getTimeToCollisionBoundary()).getYCoordinate() + Util.getSign(getVelocities().getYCoordinate())*this.getRadius()
	 * @return	Null if getTimeToCollisionBoundary() is infinite.
	 * 			|if( Math.abs(getTimeToCollisionBoundary()) > Double.MAX_VALUE)
	 * 			| 	then result==null
	 * @return	If there isn't a collision with a vertical boundary, the xColPosition has to change since the radius shouldn't be added 
	 * 			or subtracted (according to the sign of the velocity in the x-direction). That's the fact when the xColPosition isn't situated
	 * 			at the left or right boundary.
	 * 			| if( !Util.fuzzyEquals(xColPosition,0) && !Util.fuzzyEquals(getWorld().getSize().getXCoordinate(),xColPosition) )
	 * 			|		then result.getXCoordinate() == this.getPositionAfterMove(time).getXCoordinate()
	 * 			| else result.getXCoordinate() == xColPosition
	 * @return	If there isn't a collision with a horizontal boundary, the yColPosition has to change since the radius shouldn't be added 
	 * 			or subtracted (according to the sign of the velocity in the y-direction). That's the fact when the yColPosition isn't situated
	 * 			at the upper or under boundary.
	 * 			| if( !Util.fuzzyEquals(yColPosition,0) && !Util.fuzzyEquals(getWorld().getSize().getYCoordinate(),yColPosition) )
	 * 			|		then result.getYCoordinate() == this.getPositionAfterMove(time).getYCoordinate()
	 * 			| else result.getYCoordinate() == yColPosition
	 * @return  Null if the resulting position is not valid.
	 * 			| if(!canHaveAsPosition(this.getPositionAfterMove(getTimeToCollisionBoundary()))
	 * 			|	then result == null
	 * @note	This method is written totally.
	 */
	public Vector getCollisionPositionBoundary(){ 
		try{
			Double time=getTimeToCollisionBoundary();
			if(time.isInfinite()){
				return null;
			}

			Vector newPosition = this.getPositionAfterMove(time);
			double xColPosition = newPosition.getXCoordinate() + Util.getSign(getVelocities().getXCoordinate())*this.getRadius();
			double yColPosition = newPosition.getYCoordinate() + Util.getSign(getVelocities().getYCoordinate())*this.getRadius();

			if(!Util.fuzzyEquals(xColPosition,0) && !Util.fuzzyEquals(getWorld().getSize().getXCoordinate(),xColPosition)){
				xColPosition = newPosition.getXCoordinate();
			}

			if(!Util.fuzzyEquals(yColPosition,0) && !Util.fuzzyEquals(getWorld().getSize().getYCoordinate(),yColPosition)){
				yColPosition = newPosition.getYCoordinate();
			}

			Vector position = new Vector(xColPosition,yColPosition);
			if(canHaveAsPosition(newPosition)){
				return position;
			}
			else{
				return null;
			}
		}
		catch (IllegalStateException exc){
			return null;
		}
	}

	/**
	 * Terminates this spaceObject.
	 */
	public abstract void terminate();

}
